﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using DashFire;
using UnityEngine;
using UnityEditor;
using System.Text.RegularExpressions;

namespace DashFire
{
  public class ResBuildData
  {
    public int m_Id;
    public string m_ResourcesName;
    public string m_TargetName;
    public string m_ResourcesShortName;
    public string m_MD5;
    public long m_Size;
    public string m_Guid;
    public string m_DependGuid = string.Empty;
    public bool m_IsNeedBuild = false;
    public override string ToString()
    {
      return string.Format("Id:{0} Resources:{1} Resources:{2} TargetName:{3} MD5:{4} Size:{5}",
        m_Id,
        m_ResourcesName,
        m_ResourcesShortName,
        m_TargetName,
        m_MD5,
        m_Size);
    }
  }
  public class ResBuildGenerator
  {
    private static Regex s_NameRegex = new Regex(@"^[A-Za-z0-9\/._]+$");
    private static Dictionary<string, ResBuildData> s_CurContainer = new Dictionary<string, ResBuildData>();
    private static int s_IdGen = 0;
    private static bool s_IsContainerNew = false;
    private static HashSet<string> s_DiffSet = new HashSet<string>();
    private static string[] s_ExcludeExt = new string[] { ".cs", ".js" };

    public static Dictionary<string, ResBuildData> GetContainer(bool isReGen = false)
    {
      if (isReGen || !s_IsContainerNew) {
        GenAllResBuildData();
      }
      return s_CurContainer;
    }
    public static Dictionary<string, ResBuildData> GetRawContainer()
    {
      return s_CurContainer;
    }
    public static bool GenAllResBuildData()
    {
      s_IdGen = 0;
      s_CurContainer.Clear();
      ResBuildData sheetData = new ResBuildData();
      sheetData.m_Id = s_IdGen++;
      sheetData.m_ResourcesName = "ResSheet";
      sheetData.m_TargetName = ResBuildConfig.ResSheetZipPath;
      sheetData.m_ResourcesShortName = "ResSheet";
      sheetData.m_MD5 = "";
      sheetData.m_Size = 0;
      sheetData.m_Guid = "";
      sheetData.m_DependGuid = "ResSheet";
      sheetData.m_IsNeedBuild = false;
      s_CurContainer.Add(sheetData.m_ResourcesName, sheetData);

      for (int index = 0; index < ResBuildConfig.ResBuildDirConfigsCount; index++) {
        ResBuildDirConfig config = ResBuildConfig.ResBuildDirConfigs[index];
        if (config != null) {
          GenResBuildDataByConfig(config);
        }
      }
      if (!CheckDiffFile()) {
        ResBuildLog.Warn("ResBuildGenerator.CheckDiffFile LoadDiffFile failed.");
        return false;
      }
      if (!OutputResBuildFile()) {
        ResBuildLog.Warn("ResBuildGenerator.OutputResBuildFile OutputResBuildFile failed.");
        return false;
      }

      s_IsContainerNew = true;
      ResBuildLog.Info("ResBuildGenerator GenAllResBuildData Success");
      return true;
    }
    public static bool CheckDiffFile()
    {
      s_DiffSet.Clear();
      string diffPath = ResBuildHelper.FormatResDiffFilePath();
      if (string.IsNullOrEmpty(diffPath)) {
        return true;
      }
      if (!File.Exists(diffPath)) {
        ResBuildLog.Warn("ResBuildGenerator.LoadDiffFile file miss. diffPath:" + diffPath);
        return false;
      }
      string[] contentList = File.ReadAllLines(diffPath, System.Text.Encoding.UTF8);
      string tmpGuid = string.Empty;
      string tmpItemPath = string.Empty;
      if (contentList != null && contentList.Length > 0) {
        tmpGuid = string.Empty;
        tmpItemPath = string.Empty;
        foreach (string itemPath in contentList) {
          if (string.IsNullOrEmpty(itemPath)) {
            continue;
          }
          string[] content = itemPath.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
          if (content != null && content.Length > 0) {
            string contentFile = content[content.Length - 1];
            if (contentFile.StartsWith("Client") || contentFile.StartsWith("Server") || contentFile.StartsWith("Public")) {
              s_DiffSet.Add("ResSheet");
            } else if (contentFile.StartsWith("Assets")) {
              tmpGuid = AssetDatabase.AssetPathToGUID(contentFile);
              if (!string.IsNullOrEmpty(tmpGuid)) {
                s_DiffSet.Add(tmpGuid);
              }
            }
          }
        }
      }

      foreach (ResBuildData config in s_CurContainer.Values) {
        foreach (string diffItem in s_DiffSet) {
          if (config != null
            && config.m_DependGuid.Contains(diffItem)
            && !string.IsNullOrEmpty(diffItem.Trim())) {
            config.m_IsNeedBuild = true;
            ResBuildLog.Info("CheckDiffFile NeedBuild:{0} DependGuid:{1}", config.ToString(), diffItem);
          }
        }
      }

      return true;
    }
    public static bool GenResBuildData(UnityEngine.Object [] selObjs)
    {
      s_IdGen = 0;
      s_CurContainer.Clear();
      if (selObjs == null || selObjs.Length == 0) {
        ResBuildLog.Warn("GenBuildConfig GenResBuildData null");
        return false;
      }
      foreach (UnityEngine.Object curObj in selObjs) {
        if (curObj != null) {
          string selAssetPath = AssetDatabase.GetAssetPath(curObj);
          if (string.IsNullOrEmpty(selAssetPath)) {
            ResBuildLog.Warn("GenBuildConfig GenBuildConfig GetAssetPath null");
            continue;
          }
          if (ResBuildHelper.IsDirectoryRes(selAssetPath)) {
            GenResBuildDataByDir(selAssetPath, new string[] { "*.*" });
          } else {
            GenResBuildDataByFile(selAssetPath);
          }
        }
      }
      ResBuildLog.Info("ResBuildGenerator GenResBuildData Success");
      return true;
    }
    public static bool GenResSheetData()
    {
      s_IdGen = 0;
      s_CurContainer.Clear();
      ResBuildData sheetData = new ResBuildData();
      sheetData.m_Id = s_IdGen++;
      sheetData.m_ResourcesName = "ResSheet";
      sheetData.m_TargetName = ResBuildConfig.ResSheetZipPath;
      sheetData.m_ResourcesShortName = "ResSheet";
      sheetData.m_MD5 = "";
      sheetData.m_Size = 0;
      sheetData.m_Guid = "";
      sheetData.m_DependGuid = "ResSheet";
      sheetData.m_IsNeedBuild = false;
      s_CurContainer.Add(sheetData.m_ResourcesName, sheetData);
      ResBuildLog.Info("ResBuildGenerator GenResBuildData Success");
      return true;
    }
    private static bool GenResBuildDataByConfig(ResBuildDirConfig config)
    {
      string[] tResBuildDirs = config.ResBuildDirs.Split(ResBuildConfig.ConfigSplit, StringSplitOptions.RemoveEmptyEntries);
      if (tResBuildDirs == null) {
        ResBuildLog.Warn("GenBuildConfig ResBuildDirs error:" + config.ResBuildDirs);
        return false;
      }
      string[] tResBuildPattern = config.ResBuildPattern.Split(ResBuildConfig.ConfigSplit, StringSplitOptions.RemoveEmptyEntries);
      if (tResBuildPattern == null) {
        ResBuildLog.Warn("SearchDependencyByDir ResBuildPattern error:" + config.ResBuildPattern);
        return false;
      }
      foreach (string dir in tResBuildDirs) {
        if (ResBuildHelper.IsDirectoryRes(dir)) {
          GenResBuildDataByDir(dir, tResBuildPattern);
        } else if (ResBuildHelper.CheckFilePatternRegex(dir, tResBuildPattern)) {
          GenResBuildDataByFile(dir);
        } else {
          ResBuildLog.Warn("GenBuildConfig GenResBuildDataByConfig skip:" + dir);
        }
      }
      return true;
    }
    private static bool GenResBuildDataByDir(string dir, string[] tResBuildPattern)
    {
      DirectoryInfo source = new DirectoryInfo(dir);
      if (!source.Exists) {
        ResBuildLog.Warn("GenBuildConfig GenResBuildDataByDir not exist:" + dir);
        return false;
      }
      foreach (string pattern in tResBuildPattern) {
        FileInfo[] files = source.GetFiles(pattern, SearchOption.AllDirectories);
        foreach (FileInfo fInfo in files) {
          string assetPath = FormatResourceName(fInfo.FullName);
          if (string.IsNullOrEmpty(assetPath)) {
            continue;
          }
          GenResBuildDataByFile(assetPath);
        }
      }
      return true;
    }
    private static bool GenResBuildDataByFile(string assetPath)
    {
      string assetPathLower = assetPath.ToLower();
      if (s_CurContainer.ContainsKey(assetPathLower)) {
        ResBuildLog.Warn("GenBuildConfig GenResBuildDataByFile exist:" + assetPath);
        return false;
      }
      ResBuildData data = new ResBuildData();
      data.m_Id = s_IdGen++;
      data.m_ResourcesName = assetPathLower;
      if (!s_NameRegex.IsMatch(assetPathLower)) {
        ResBuildLog.Warn("PathError:" + assetPath);
      }
      data.m_TargetName = FormatTargetName(data.m_ResourcesName);
      data.m_ResourcesShortName = FormatResourceShortName(assetPath);
      data.m_Size = 0;
      data.m_MD5 = string.Empty;
      data.m_IsNeedBuild = false;
      data.m_Guid = AssetDatabase.AssetPathToGUID(assetPath);
      data.m_DependGuid = GetAssetDependency(assetPath);
      s_CurContainer.Add(assetPathLower, data);
      return true;
    }
    private static string FormatTargetName(string assetPath)
    {
      string guid = AssetDatabase.AssetPathToGUID(assetPath);
      string dirPath = guid.Substring(0, 2);
      return string.Format("{0}/{1}{2}", dirPath, guid, ResBuildConfig.BuildOptionExtend);
    }
    private static string FormatResourceName(string assetPath)
    {
      assetPath = assetPath.Replace("\\", "/");
      if (string.IsNullOrEmpty(assetPath)) {
        ResBuildLog.Warn("TranslateFilePathToAssetPath filePath invalid filePath:" + assetPath);
        return string.Empty;
      }
      assetPath = assetPath.Trim();
      int assetsIndex = assetPath.ToLower().IndexOf("assets");
      if (assetsIndex < 0) {
        ResBuildLog.Warn("TranslateFilePathToAssetPath filePath invalid filePath:" + assetPath);
        return string.Empty;
      }
      return assetPath.Substring(assetsIndex);
    }
    private static string FormatResourceShortName(string assetPath)
    {
      if (assetPath.EndsWith(".unity")) {
        return Path.GetFileNameWithoutExtension(assetPath);
      } else {
        int startIndex = assetPath.IndexOf("Resources/");
        startIndex += 10;
        int endIndex = assetPath.LastIndexOf(".");
        if (startIndex >= 0 && endIndex >= 0) {
          return assetPath.Substring(startIndex, (endIndex - startIndex));
        } else if (endIndex >= 0) {
          return assetPath.Substring(0, (endIndex - 0));
        } else {
          return assetPath;
        }
      }
    }
    private static string GetAssetDependency(string assetPath)
    {
      string ret = string.Empty;
      string tmpGuid = string.Empty;
      bool isExclude = false;
      string[] dependList = AssetDatabase.GetDependencies(new string[] { assetPath });
      if (dependList != null && dependList.Length > 0) {
        foreach (string itemPath in dependList) {
          isExclude = false;
          tmpGuid = string.Empty;
          foreach (string ext in s_ExcludeExt) {
            if (itemPath.EndsWith(ext)) {
              isExclude = true;
              break;
            }
          }
          if (!isExclude) {
            tmpGuid = AssetDatabase.AssetPathToGUID(itemPath);
            ret += tmpGuid + ";";
          }
        }
      }
      return ret;
    }
    private static bool OutputResBuildFile()
    {
      string filePath = ResBuildHelper.FormatResBuildFilePath();
      string fileContent = ResBuildConfig.ResBuildConfigHeader + "\n";
      foreach (ResBuildData config in s_CurContainer.Values) {
        if (config != null) {
          string abInfo = string.Format(ResBuildConfig.ResBuildConfigFormat + "\n",
            config.m_Id,
            config.m_ResourcesName,
            config.m_ResourcesShortName,
            config.m_TargetName,
            config.m_MD5,
            config.m_Size,
            config.m_Guid,
            config.m_IsNeedBuild,
            config.m_DependGuid
            );
          fileContent += abInfo;
        }
      }
      try {
        if (!ResBuildHelper.CheckFilePath(filePath)) {
          ResBuildLog.Warn("ResBuildGenerator.OutputResBuildFile file not exist.");
          return false;
        }
        File.WriteAllText(filePath, fileContent, Encoding.UTF8);
      } catch (System.Exception ex) {
        ResBuildLog.Warn("ResBuildGenerator.OutputResBuildFile failed!" + ex);
        return false;
      }
      AssetDatabase.Refresh();
      ResBuildLog.Info("ResBuildGenerator.OutputResBuildFile Success");
      return true;
    }
    public static List<ResBuildData> SearchResByName(Dictionary<string, ResBuildData> container, string namePrefix)
    {
      List<ResBuildData> dataList = new List<ResBuildData>();
      foreach (ResBuildData data in container.Values) {
        if (data.m_ResourcesShortName.ToLower() == namePrefix.ToLower()) {
          dataList.Add(data);
        }
      }
      return dataList;
    }
    public static List<ResBuildData> SearchResByNamePostfix(Dictionary<string, ResBuildData> container, string namePostfix)
    {
      List<ResBuildData> dataList = new List<ResBuildData>();
      foreach (ResBuildData data in container.Values) {
        if (data.m_ResourcesName.EndsWith(namePostfix)) {
          dataList.Add(data);
        }
      }
      return dataList;
    }
  }
}
